using System;
using System.Collections;
using System.Collections.Generic;

namespace LightCAD.MathLib
{
    public class Line3
    {
        #region scope properties or methods
        //private static Vector3 _startP = new Vector3();
        //private static Vector3 _startEnd = new Vector3();
        private static Line3Context GetContext()
        {
            return ThreadContext.GetCurrThreadContext().Line3Ctx;
        }
        #endregion

        #region Properties

        public Vector3 Start;
        public Vector3 End;

        #endregion

        #region constructor
        public Line3(Vector3 start = null, Vector3 end = null)
        {
            if (start == null) start = new Vector3();
            if (end == null) end = new Vector3();
            this.Start = start;
            this.End = end;
        }
        #endregion

        #region methods
        public Line3 Set(Vector3 start, Vector3 end)
        {
            this.Start.Copy(start);
            this.End.Copy(end);
            return this;
        }
        public Line3 Copy(Line3 line)
        {
            this.Start.Copy(line.Start);
            this.End.Copy(line.End);
            return this;
        }
        public Vector3 GetCenter(Vector3 target = null)
        {
            target = target ?? new Vector3();
            return target.AddVectors(this.Start, this.End).MulScalar(0.5);
        }
        public Vector3 Delta(Vector3 target = null)
        {
            target = target ?? new Vector3();
            return target.SubVectors(this.End, this.Start);
        }
        public double DistanceSq()
        {
            return this.Start.DistanceToSquared(this.End);
        }
        public double Distance()
        {
            return this.Start.DistanceTo(this.End);
        }
        public Vector3 At(double t, Vector3 target)
        {
            return this.Delta(target).MulScalar(t).Add(this.Start);
        }
        public double ClosestPointToPointParameter(Vector3 point, bool clampToLine)
        {
            var _startP = GetContext()._startP;
            var _startEnd = GetContext()._startEnd;
            _startP.SubVectors(point, this.Start);
            _startEnd.SubVectors(this.End, this.Start);
            double startEnd2 = _startEnd.Dot(_startEnd);
            double startEnd_startP = _startEnd.Dot(_startP);
            double t = startEnd_startP / startEnd2;
            if (clampToLine)
            {
                t = MathEx.Clamp(t, 0, 1);
            }
            return t;
        }
        public Vector3 ClosestPointToPoint(Vector3 point, bool clampToLine, Vector3 target)
        {
            double t = this.ClosestPointToPointParameter(point, clampToLine);
            return this.Delta(target).MulScalar(t).Add(this.Start);
        }
        public Line3 ApplyMatrix4(Matrix4 matrix)
        {
            this.Start.ApplyMatrix4(matrix);
            this.End.ApplyMatrix4(matrix);
            return this;
        }
        public virtual bool Equals(Line3 line)
        {
            return line.Start.Equals(this.Start) && line.End.Equals(this.End);
        }
        public virtual Line3 Clone()
        {
            return new Line3().Copy(this);
        }
        #endregion

    }
    public sealed class Line3Context
    {
        public readonly Vector3 _startP = new Vector3();
        public readonly Vector3 _startEnd = new Vector3();
    }
}
